package controllers;

import java.util.ArrayList;
import java.util.HashMap;

import model.CategoryManager;

import buttons.Button;
import buttons.control.C_Button;
import buttons.control.C_ToggleButton;
import buttons.view.V_Button;
import buttons.view.V_DoubleSlider;
import utilities.EventListener;
import utilities.EventOwner;
import viz_main.BarComponent;
import viz_main.Node;
import viz_main.ScatterPlotCAxis;
import viz_ui.BarView;
import viz_ui.TreeMapView;

// Handles interaction with the bar view such as selecting a single bar or mouse-over tooltips of a bar
public class C_BarView extends EventOwner implements EventListener{
	
	protected C_TooltipControl c_toolTip;
	protected String tooltipText = "";
	private int selectedIndex = -1;
	private int overIndex = -1;
	private BarView v;
	private C_TreeMap treeViewControl = null;
	//private int reducedMax;
	//private int 

	public C_BarView(BarView view, ControlManager manager, C_TooltipControl toolTipController)
	{
		vFeature = view;
		v = view;
		c_toolTip = toolTipController;
		controlManager = manager;
		controlManager.addController(this);
		// get all the buttons created by the view object, 
		// add this controller as a subscriber so that it can handle the interaction logic
		Button[] buttons = view.getButtons();
		for(int i = 0; i < buttons.length; i++)
		{
			buttons[i].addSubscriber(this);
		}
	}
	
	@Override
	public void onInteract (float mouseXLoc, float mouseYLoc, String interID)
	{
		BarView barView = (BarView)vFeature;
		overIndex = -1;
		// if the mouse is within the interactive portion of the bar view...
		if(mouseXLoc > barView.getXStart() && mouseXLoc < barView.getXEnd())
		{
			if(mouseYLoc > barView.getYLoc()+barView.getYEnd() && mouseYLoc < barView.getYStart())
			{
				// Perform a "Nearest Neighbor" calculation to determine selection:
				// first get the percent of the inner window that the mouse x position is at:
				float xPercent = (mouseXLoc-barView.getXStart())/(barView.getXEnd() - barView.getXStart());
				// multiply that by the length of the array of bars.
				int indexEstimate = (int) (barView.getFilteredArrayLength()*xPercent);
				// set the over index to be the index of that bar
				overIndex = indexEstimate;
			}
		}
		
		if(interID.equals("leftClick"))
		{
			// TODO:
			// Only de-select something from the tree map if the user has clicked on something in 
			// this view...otherwise allow them to change values and use the UI buttons without 
			// preforming any de-selection.			
			if(overIndex > -1)
			{
				if(overIndex != selectedIndex)
				{
					deselectAll();
					barView.getAllBars()[overIndex+v.getFilteredStartIndex()].setSelected(true);
					selectedIndex = overIndex+v.getFilteredStartIndex();
					triggerEvent("@Selected");
					controlManager.reDraw();
				}
				// if selecting the same object as previous, then toggle it off
				else {
					deselectAll();
					controlManager.reDraw();
				}
			}
			// if clicking outside of the view, turn off selection
			else {
				deselectAll();
				controlManager.reDraw();
			}
		}
		
		handleTooltip(mouseXLoc, mouseYLoc);
	}
	
	public void deselectAll()
	{
		if(selectedIndex > -1)
		{
			((BarView)vFeature).getAllBars()[selectedIndex].setSelected(false);
			selectedIndex = -1;
			triggerEvent("@Deselected");
		}
	}
	
	// TODO: revise this logic
	private void handleTooltip(float mouseXLoc, float mouseYLoc)
	{
		if(c_toolTip.checkForTip(mouseXLoc, mouseYLoc))
        {
            if(tooltipText.length() > 0) // if the button HAS a set tooltip display it
            {
              c_toolTip.setName(tooltipText);
              c_toolTip.setActive(true);
              controlManager.reDraw();
            }
        }
        else c_toolTip.setActive(false);
	}

	@Override
	// Handle events triggered by the buttons of this controllers vFeature.
	// this includes filtering and switching the x-axis.
	public void handleEvent(String trigger, EventOwner obj) {
		BarView barView = (BarView)vFeature;
		
		// any trigger that starts with the @ character refers to a C_TreeMap event
		if(trigger.startsWith("@"))
		{
			C_TreeMap treeMapControl = (C_TreeMap) obj;
			System.out.println("TreeMap says: " + trigger);
			treeViewControl = treeMapControl;
			if(trigger.equals("@Selected"))
			{
				Node selectedNode = treeMapControl.getSelectedNode();
				// find the unique categories that are included within these indexes:
				highlightBars(selectedNode);
			}
			
			else if(trigger.equals("@InnerSelected")){
				Node selectedNode = treeMapControl.getInnerSelectedNode();
				// find the unique categories that are included within these indexes:
				highlightBars(selectedNode);
			}
			
			else if(trigger.equals("@Deselected"))
			{
				for(int j = 0; j < barView.getAllBars().length; j++)
				{
					barView.getAllBars()[j].resetHeight();
				}
				rescaleBars();
			}
		}
		
		// We can assume that any other trigger will likely be an internal button event
		else
		{
			  HashMap axisList = barView.getCAxisList();
		      String buttonName = ( (V_Button)((C_Button)obj).getView() ).getName();
		      
		      if(buttonName.equals("xAxis"))
		      {
		    	deselectAll();
		    	overIndex = -1;
		    	barView.setXAxis((ScatterPlotCAxis)axisList.get(trigger));	    	
		    	barView.generateBars();
		    	barView.updateBars();
		    	// Take into account that they may have a node selected in the tree map when 
		    	// the axis is switched:
		    	if(treeViewControl != null)
		    	{
		    		highlightBars(treeViewControl.getAnySelectedNode());
		    	}
		      }
		      
		      // TODO: provide filtering interaction
		      else if(buttonName.equals("xFilter"))
		      {
		        V_DoubleSlider tmpButton = (V_DoubleSlider)((C_Button)obj).getView();
		        if(trigger.equals("moveLeftSlider")) {
		        	barView.updateInterval();
		        	barView.updateBars();
		        }
		        else if(trigger.equals("moveRightSlider")) {
		        	barView.updateInterval();
		        	barView.updateBars();
		        }
		        else if(trigger.equals("moveBothSliders")) {
		        	barView.updateInterval();
		        	barView.updateBars();
		        }
		      }
		      
		      else if(buttonName.equals("scale attributes"))
		      {
		    	  System.out.println("request scale modification");
		    	  // Re-scale the bar chart so that the max height of the bars is based on the current max
		    	  // of the filtered information
		    	  C_ToggleButton buttonControl = ((C_ToggleButton)obj);
		    	  System.out.println(buttonControl.getToggle());
		    	  
		    	  rescaleBars();
		      }
		      
		      controlManager.reDraw();
		}
	}
	
	// preform a re-scale on the bars either to return them to global ticket scale or to reduce
	// their size to show only their local relationships
	// this is done independently from highlighting but most often the 2 are done at the same time.
	public void rescaleBars()
	{
		  BarView barView = (BarView)vFeature;
		  if(barView.getScaleToggleValue() == true)
	  	  {
		  		// first we need to calculate the reduced max:
		  	  	int max = 0;
			    for(int i = 0; i < barView.getAllBars().length; i++){
			    	if(max < barView.getAllBars()[i].getReducedAmount()){
			    		max = barView.getAllBars()[i].getReducedAmount();
			    	}
			    }
			    // set the new maximum value to be displayed on the y-axis:
			    // a percentage will be passed to the bars to scale them appropriately
			    System.out.println("new max: " + max);
			    barView.setMax(max);
	  	  }
	  	  else {
	  		  barView.resetMax();
	  	  }
	}
	
	public void highlightBars(Node selectedNode)
	{
		if(selectedNode != null)
		{
			BarView barView = (BarView)vFeature;
			int[] indexList = selectedNode.getIndexList();
			CategoryManager categoryHandler = new CategoryManager();
			for(int i = 0; i < indexList.length; i++)
			{
				int index = indexList[i];
				categoryHandler.addElement(barView.getXAxis().getElementAsString(index), index);
			}
			
			ArrayList<String> uniqueBarCategories = barView.getXAxis().getUniqueCategories();
	
			// figure what bars to make transparent 
			for(int j = 0; j < uniqueBarCategories.size(); j++)
			{
				// if our unique category in the barView does not match up with one of the categories in the 
				// newly generated list of categories (based on what is selected in the tree map)
				if(categoryHandler.getCategoryMap().get(uniqueBarCategories.get(j)) == null)
				{
					barView.getAllBars()[j].setReducedHeight(0);
					barView.getAllBars()[j].setReducedValue(0);
				}
				else {
					float amount = categoryHandler.getCategoryMap().get(uniqueBarCategories.get(j)).length;
					float prevAmount = barView.getXAxis().getCategoryMap().get(barView.getXAxis().getUniqueCategories().get(j)).length;
					float percent = amount/prevAmount;
					barView.getAllBars()[j].setReducedHeight(barView.getAllBars()[j].getYSize()*percent);
					barView.getAllBars()[j].setReducedValue((int)amount);
				}
			}
			
			// every time a new node is selected, we need to re-compute the inner max value of those tickets
			// so that we can scale things up
			rescaleBars();
		}
	}
	
	public String getSelectedCategory()
	{
		BarView barView = (BarView)vFeature;
		return barView.getXAxis().getUniqueCategories().get(selectedIndex);
	}
	
	public ScatterPlotCAxis getActiveAxis()
	{
		BarView barView = (BarView)vFeature;
		return barView.getXAxis();
	}
}
